/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.easyway.lists;

import java.io.Serializable;
import java.util.ArrayList;

import org.easyway.interfaces.base.IBaseObject;
import org.easyway.interfaces.base.IList;
import org.easyway.objects.BaseObject;
import org.easyway.utils.Utility;

/**
 * 
 * @author Daniele Paggi
 * 
 * Base List of engine
 */
public class BaseList<T extends IBaseObject> extends BaseObject implements
		IList<T>,
		Serializable {

	private static final long serialVersionUID = 5871481455153337939L;

	protected final int SIZE_STACK = 100;

	/**
	 * list of objects
	 */
	protected ArrayList<T> list;

	/**
	 * stack index
	 */
	private int stackIndex;

	/**
	 * the stak
	 */
	protected int stack[];

	/**
	 * index used for scanning the list
	 */
	protected int indexScan;

	/** creates a new instance of Base List */
	public BaseList() {
		list = new ArrayList<T>(200); // dim. iniz. \ incremento
		indexScan = -1;
		stackIndex = -1;
		type.set("$_BASELIST");
	}

	/** creates a new instance of Base List */
	public BaseList(boolean toadd) {
		super(toadd);
		list = new ArrayList<T>(200); // dim. iniz. \ incremento
		indexScan = -1;
		stackIndex = -1;
		type.set("$_BASELIST");
	}

	/** adds an object to the list */
	public void add(T obj) {
		list.add(obj);
		obj.getUsedLists().add(this);
		if (indexScan == size() - 1) // 0.1.5
			++indexScan; // 0.1.5
	}

	/** add an object to the list */
	public void add(T obj, int index) {
		if (index < 0)
			return;
		list.add(index, obj);
		obj.getUsedLists().add(this);
		if (indexScan >= index)
			++indexScan;
	}

	/** return the object of index position */
	public T get(int index) {
		if (index >= 0 && index < list.size())
			return (T) list.get(index);
		else {
			Utility.error("index out of range: " + index + "/" + list.size(),
				"BaseList.get(int)");
			return null;
		}
	}

	/** return the position of object in the list */
	// public int get(IBaseObject obj) {
	// return list.indexOf(obj);
	// }
	/** remove the object from the list */
	public void remove(int index) {
		list.get(index).getUsedLists().remove(this);
		list.remove(index);
		if (indexScan > index) {// 0.1.8
			--indexScan;
			// if (stack != null && stackIndex > -1)
		}
		if (stack != null)
			testDecIndex(index);
	}

	/** remove the object from the list */
	public void remove(IBaseObject obj) {
		// remove(Lista.indexOf(obj));
		obj.getUsedLists().remove(this);
		int index = indexOf(obj);// list.indexOf(obj);
		if (index == -1) {
			System.out.println("object not found!");
			new Exception().printStackTrace();
		}
		list.remove(index);
		if (indexScan > index) { // 0.1.8
			--indexScan;
			// if (stack != null && stackIndex > -1)
		}
		if (stack != null)
			testDecIndex(index);
	}

	/**
	 * removes all object of type specified
	 * 
	 * @see #destroy(String)
	 */
	// public void remove(Type type) {
	// for (int i = size() - 1; i >= 0; --i)
	// if (get(i).getType().equals(type))
	// remove(i);
	// }
	protected void testDecIndex(int index) {
		for (int i = stackIndex; i >= 0; --i) {
			if (stack[i] > index)
				--stack[i];
		}
	}

	/** destroy all objects from the list */
	public void destroyAll() {
		// BaseObject temp;
		startScan();// 0.1.8
		while (next()) {// 0.1.8
			// for (indexScan = size() - 1; indexScan >= 0; --indexScan) {
			// //0.1.8//
			// 0.1.6
			getCurrent().destroy(); // 0.1.8
		}
		indexScan = -1; // 0.1.5
	}

	/** remove all objects from the list */
	public void removeAll() {
		// list = new Vector<BaseObject>();// 0.1.8//0.1.5
		for (int i = size() - 1; i >= 0; --i) { // 0.1.8 // 015
			remove(i);// 0.1.8//0.1.5
		}// 0.1.8//0.1.5
		indexScan = -1;
	}

	/** sets the position of an object */
	public void set(int index, T obj) {
		list.set(index, obj);
	}

	/** returns the numeber of objects that compose the list */
	public int size() {
		return list.size();
	}

	/** returns if the list contains or not the object */
	public int contains(T obj) {
		return indexOf(obj);
	}

	/** counts the number of object of the type "type" */
	// public int count(String type) {
	// int c = 0;
	// for (int i = 0; i < list.size(); ++i)
	// if (((BaseObject) list.elementAt(i)).getType().compareTo(type) == 0)
	// ++c;
	// return c;
	// }
	/** rimuove tutti gli object di tipo "tipo" */
	// public void destroy(String type) {
	// BaseObject temp;
	// // for (int i = 0; i < size(); ++i) // 0.16
	// for (indexScan = size() - 1; indexScan >= 0; --indexScan)
	// // 0.1.6
	// if ((temp = get(indexScan)) != null
	// && temp.getType().compareTo(type) == 0)
	// temp.destroy();
	// indexScan = -1; // 0.1.5
	// }
	/** ritorna un vettore con tutti gli object di Tipo "tipo" */
	// public void get(String type, Vector<IBaseObject> v) {
	// for (int i = 0; i < size(); ++i)
	// if (get(i).getType().compareTo(type) == 0)
	// v.add(list.elementAt(i));
	// }
	/** adds elements from another list */
	public <K extends IList<T>> void addElementsFrom(K list2) {
		// removeAll(); // XXX: Bug?
		// for (int i = 0; i < L2.size(); ++i)
		for (int i = list2.size() - 1; i >= 0; --i)
			add(list2.get(i));
	}

	/**
	 * automated stanning:<br>
	 * <br>
	 * baseList.startScan();<br>
	 * while(baseList.next() ) {<br>
	 * IBaseObject spr = baseList.getCurrent();<br>
	 * ...<br> }<br>
	 * <br>
	 */
	public void startScan() {
		if (indexScan != -1)
			pushIndexScan();
		indexScan = getLastIndex() + 1;
	}

	/**
	 * automated stanning:<br>
	 * <br>
	 * baseList.startScan( baseList.size()/2 );<br>
	 * while(baseList.next() ) {<br>
	 * IBaseObject spr = baseList.getCurrent();<br>
	 * ...<br> }<br>
	 * <br>
	 */
	@Deprecated public void startScan(int index) {
		if (indexScan != -1)
			pushIndexScan();
		indexScan = index;// this.size();
	}

	/**
	 * automated stanning:<br>
	 * <br>
	 * baseList.startScan();<br>
	 * while(baseList.next() ) {<br>
	 * IBaseObject spr = baseList.getCurrent();<br>
	 * ...<br> }<br>
	 * <br>
	 */
	public boolean next() {
		--indexScan;
		// if (indexScan >= size()) {
		// indexScan = size() - 1;
		// }
		if (indexScan >= 0)
			return true;
		if (stack != null && stackIndex > -1)
			popIndexScan();
		return false;
	}

	/**
	 * automated stanning:<br>
	 * <br>
	 * baseList.startScan();<br>
	 * while(baseList.next() ) {<br>
	 * IBaseObject spr = baseList.getCurrent();<br>
	 * ...<br> }<br>
	 * <br>
	 */
	public T getCurrent() {
		T temp = get(indexScan);
		if (temp == null) {
			Utility.error("finded a null object in BaseList",
				"BaseList.getCurrent");
		}
		return temp;
	}
	
	public void breakScan() {
		stackIndex = -1;
		if (stack != null && stackIndex > -1)
			popIndexScan();
	}

	protected void pushIndexScan() {
		if (stack == null) {
			stack = new int[SIZE_STACK];
			stackIndex = -1;
		}
		++stackIndex;
		stack[stackIndex] = indexScan;
	}

	protected void popIndexScan() {
		indexScan = stack[stackIndex];
		--stackIndex;
	}

	public int indexOf(IBaseObject obj) {
		return list.indexOf(obj);
		// for (int i=list.size()-1; i>=0; --i) {
		// if (list.get(i)==obj)
		// return i;
		// }
		// return -1;
	}

	/**
	 * returns the last valid index
	 * 
	 * @return
	 */
	public int getLastIndex() {
		return size() - 1;
	}

	/**
	 * returns the first valid index
	 * 
	 * @return
	 */
	public int getFirstIndex() {
		if (size() >= 0)
			return 0;
		return -1;
	}

	/**
	 * sets the list of this object to the object 'list'
	 * 
	 */
	protected void setList(BaseList<T> list) {
		this.list = list.list;
		if (indexScan != -1) {
			indexScan = getLastIndex() + 1;
		}
		// TODO : TEST for the stackIndex!!
		stackIndex = -1;		
	}

}
